<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CBMSIO类说明文档</title>
    <style>
        body {
            font-family: 'Microsoft YaHei', Arial, sans-serif;
            line-height: 1.6;
            color: #333;
            max-width: 1000px;
            margin: 0 auto;
            padding: 20px;
        }
        h1 {
            color: #0066cc;
            border-bottom: 2px solid #0066cc;
            padding-bottom: 10px;
        }
        h2 {
            color: #0066cc;
            border-bottom: 1px solid #ddd;
            padding-bottom: 5px;
            margin-top: 30px;
        }
        h3 {
            color: #333;
            margin-top: 20px;
        }
        pre {
            background-color: #f5f5f5;
            padding: 10px;
            border-radius: 5px;
            overflow-x: auto;
        }
        code {
            font-family: Consolas, Monaco, 'Andale Mono', monospace;
            background-color: #f5f5f5;
            padding: 2px 4px;
            border-radius: 3px;
        }
        table {
            border-collapse: collapse;
            width: 100%;
            margin: 20px 0;
        }
        th, td {
            border: 1px solid #ddd;
            padding: 8px 12px;
            text-align: left;
        }
        th {
            background-color: #f2f2f2;
        }
        .note {
            background-color: #fffacd;
            padding: 10px;
            border-left: 4px solid #ffcc00;
            margin: 15px 0;
        }
        .warning {
            background-color: #ffebee;
            padding: 10px;
            border-left: 4px solid #f44336;
            margin: 15px 0;
        }
        .method {
            margin-bottom: 20px;
        }
        .method-signature {
            font-weight: bold;
        }
        .param-table {
            margin-left: 20px;
        }
    </style>
</head>
<body>
    <h1>CBMSIO类说明文档</h1>
    
    <h2>1. 类概述</h2>
    <p>CBMSIO类是一个基于QThread的线程类，主要用于处理BA项目中的IO操作。该类负责读取和控制数字输入(DI)和数字输出(DO)信号，支持两种硬件接口方式：</p>
    <ul>
        <li>通过系统文件接口直接操作GPIO</li>
        <li>通过华田屏幕SDK提供的GPIO接口进行操作</li>
    </ul>
    <p>该类实现了对不同硬件平台的兼容，包括华田7寸屏、华田10寸屏和南北山设备，可以根据硬件OEM ID自动选择合适的IO操作方式。</p>
    
    <div class="note">
        <p><strong>注意：</strong>CBMSIO类版本为0.1，主要用于BA-10-V3.8.0项目。</p>
    </div>

    <h2>2. 类继承关系</h2>
    <p>CBMSIO继承自QThread类，这使得IO操作可以在独立的线程中执行，避免阻塞主线程。</p>
    <pre><code>class CBMSIO : public QThread</code></pre>

    <h2>3. 成员变量说明</h2>
    <table>
        <tr>
            <th>变量名</th>
            <th>类型</th>
            <th>说明</th>
        </tr>
        <tr>
            <td>m_pSerial</td>
            <td>CXnSerial*</td>
            <td>串口通信对象指针，用于与IO设备进行Modbus通信</td>
        </tr>
        <tr>
            <td>m_pIOModule</td>
            <td>CIOModule*</td>
            <td>IO模块对象指针，用于存储DI/DO状态</td>
        </tr>
        <tr>
            <td>m_bSerialOK</td>
            <td>bool</td>
            <td>串口是否正常打开的标志</td>
        </tr>
        <tr>
            <td>m_strPortName</td>
            <td>QString</td>
            <td>串口名称，Windows下默认为"com4"，Linux下默认为"/dev/ttyS8"</td>
        </tr>
        <tr>
            <td>m_iBaudRate</td>
            <td>int</td>
            <td>波特率，默认为115200</td>
        </tr>
        <tr>
            <td>m_iDataBits</td>
            <td>int</td>
            <td>数据位，默认为8</td>
        </tr>
        <tr>
            <td>m_iStopBits</td>
            <td>int</td>
            <td>停止位，默认为1</td>
        </tr>
        <tr>
            <td>m_iParity</td>
            <td>int</td>
            <td>校验位，默认为0（无校验）</td>
        </tr>
        <tr>
            <td>m_iRtscts</td>
            <td>int</td>
            <td>硬件流控制标志</td>
        </tr>
        <tr>
            <td>m_bPrintMsg</td>
            <td>bool</td>
            <td>是否打印调试信息的标志</td>
        </tr>
        <tr>
            <td>m_iSerialDataLen</td>
            <td>int</td>
            <td>串口数据长度</td>
        </tr>
        <tr>
            <td>m_pSerialDataBuf</td>
            <td>char*</td>
            <td>串口数据缓冲区</td>
        </tr>
        <tr>
            <td>m_logCount</td>
            <td>int</td>
            <td>日志计数器，用于限制日志输出数量</td>
        </tr>
    </table>

    <h2>4. 主要方法分析</h2>
    
    <div class="method">
        <h3>4.1 构造函数和析构函数</h3>
        <div class="method-signature">CBMSIO::CBMSIO(QObject *parent = 0)</div>
        <p>构造函数初始化所有成员变量，创建串口对象和数据缓冲区。</p>
        
        <div class="method-signature">CBMSIO::~CBMSIO()</div>
        <p>析构函数释放串口对象和数据缓冲区的内存。</p>
    </div>
    
    <div class="method">
        <h3>4.2 初始化方法</h3>
        <div class="method-signature">int CBMSIO::init()</div>
        <p>初始化IO模块，获取全局IO模块指针。返回值：0表示成功，负值表示失败。</p>
    </div>
    
    <div class="method">
        <h3>4.3 线程执行方法</h3>
        <div class="method-signature">void CBMSIO::run()</div>
        <p>线程的主执行函数，实现了以下功能：</p>
        <ul>
            <li>根据硬件类型（通过KgGlobalData::ms_hardwareOemID判断）选择不同的IO操作方式</li>
            <li>循环读取DI状态并更新到m_pIOModule->usDI数组</li>
            <li>循环读取DO状态并更新到m_pIOModule->usDOState数组</li>
            <li>当DO控制值(usDOCtrl)与DO状态值(usDOState)不一致时，执行DO控制操作</li>
            <li>根据硬件类型选择使用GpioFactory或系统文件接口进行IO操作</li>
        </ul>
        <p>该方法是一个无限循环，每次循环后会休眠500毫秒。</p>
    </div>
    
    <div class="method">
        <h3>4.4 GPIO操作相关方法</h3>
        <div class="method-signature">int get_gpio(int num)</div>
        <p>读取指定编号的DI状态。通过系统文件接口实现。</p>
        <table class="param-table">
            <tr>
                <th>参数</th>
                <th>说明</th>
            </tr>
            <tr>
                <td>num</td>
                <td>DI编号，从1开始</td>
            </tr>
        </table>
        <p>返回值：1表示高电平，0表示低电平，-1表示读取失败</p>
        
        <div class="method-signature">int get_gpdo(int num)</div>
        <p>读取指定编号的DO状态。通过系统文件接口实现。</p>
        <table class="param-table">
            <tr>
                <th>参数</th>
                <th>说明</th>
            </tr>
            <tr>
                <td>num</td>
                <td>DO编号，从1开始</td>
            </tr>
        </table>
        <p>返回值：1表示高电平，0表示低电平，-1表示读取失败</p>
        
        <div class="method-signature">int set_gpio(int num, int status)</div>
        <p>设置指定编号的DO状态。通过系统文件接口实现。</p>
        <table class="param-table">
            <tr>
                <th>参数</th>
                <th>说明</th>
            </tr>
            <tr>
                <td>num</td>
                <td>DO编号，从1开始</td>
            </tr>
            <tr>
                <td>status</td>
                <td>要设置的状态，1表示高电平，0表示低电平</td>
            </tr>
        </table>
        <p>返回值：设置的状态值，-1表示设置失败</p>
    </div>
    
    <div class="method">
        <h3>4.5 Modbus通信相关方法</h3>
        <div class="method-signature">int CBMSIO::formDatagram(int iType, int iModbusID, int iDOIndex = 0, quint16 usDOCtrlVal = 0)</div>
        <p>根据指定的类型和参数，构造Modbus通信报文。</p>
        <table class="param-table">
            <tr>
                <th>参数</th>
                <th>说明</th>
            </tr>
            <tr>
                <td>iType</td>
                <td>操作类型：1-读DI，2-写DO，3-读DO</td>
            </tr>
            <tr>
                <td>iModbusID</td>
                <td>Modbus设备ID</td>
            </tr>
            <tr>
                <td>iDOIndex</td>
                <td>DO索引（仅在写DO时使用）</td>
            </tr>
            <tr>
                <td>usDOCtrlVal</td>
                <td>DO控制值（仅在写DO时使用）</td>
            </tr>
        </table>
        <p>返回值：构造的报文长度，负值表示失败</p>
        
        <div class="method-signature">int CBMSIO::analyDatagram(int iType, int iModbusID, char* pDataBuf, int iLen)</div>
        <p>解析Modbus通信响应报文，并更新IO状态。</p>
        <table class="param-table">
            <tr>
                <th>参数</th>
                <th>说明</th>
            </tr>
            <tr>
                <td>iType</td>
                <td>操作类型：1-读DI，2-写DO，3-读DO</td>
            </tr>
            <tr>
                <td>iModbusID</td>
                <td>Modbus设备ID</td>
            </tr>
            <tr>
                <td>pDataBuf</td>
                <td>接收到的数据缓冲区</td>
            </tr>
            <tr>
                <td>iLen</td>
                <td>数据长度</td>
            </tr>
        </table>
        <p>返回值：解析结果，负值表示失败</p>
        
        <div class="method-signature">void CBMSIO::readDataFromSerial()</div>
        <p>从串口读取数据到缓冲区。</p>
        
        <div class="method-signature">int CBMSIO::prepareExcepDatagram(uchar uaExcepCode)</div>
        <p>准备Modbus异常响应报文。</p>
        <table class="param-table">
            <tr>
                <th>参数</th>
                <th>说明</th>
            </tr>
            <tr>
                <td>uaExcepCode</td>
                <td>异常代码</td>
            </tr>
        </table>
        <p>返回值：构造的报文长度</p>
    </div>
    
    <div class="method">
        <h3>4.6 辅助方法</h3>
        <div class="method-signature">void CBMSIO::printSendInfo(char* pDataBuf, int iLen)</div>
        <p>打印发送的数据报文（十六进制格式）。</p>
        
        <div class="method-signature">void CBMSIO::printReceInfo(char* pDataBuf, int iLen)</div>
        <p>打印接收的数据报文（十六进制格式）。</p>
        
        <div class="method-signature">int CBMSIO::addParam2Buffer(char* pData, quint16 uiParamVal)</div>
        <p>将16位参数添加到缓冲区（高字节在前）。</p>
        
        <div class="method-signature">int CBMSIO::getParamFromBuffer(char* pData, quint16& uiParamVal)</div>
        <p>从缓冲区获取16位参数（高字节在前）。</p>
        
        <div class="method-signature">int CBMSIO::addModbusCRC2Buffer(char *pData, int iLen)</div>
        <p>计算Modbus CRC校验码并添加到缓冲区。</p>
        
        <div class="method-signature">quint16 CBMSIO::Modbus_CRC16(char *pData, quint16 usLen)</div>
        <p>计算Modbus CRC16校验码。</p>
    </div>

    <h2>5. 通信协议说明</h2>
    <p>CBMSIO类支持两种通信方式：</p>
    
    <h3>5.1 系统文件接口</h3>
    <p>通过Linux系统文件接口直接操作GPIO：</p>
    <ul>
        <li>DI读取：通过写入<code>/sys/class/dido/di/di_io</code>和读取<code>/sys/class/dido/di/di</code>实现</li>
        <li>DO读取：通过写入<code>/sys/class/dido/do/do_io</code>和读取<code>/sys/class/dido/do/do</code>实现</li>
        <li>DO控制：通过写入<code>/sys/class/dido/do/do_io</code>和<code>/sys/class/dido/do/do</code>实现</li>
    </ul>
    
    <h3>5.2 Modbus RTU协议</h3>
    <p>通过串口使用Modbus RTU协议与IO设备通信：</p>
    <ul>
        <li>读DI：功能码0x02，起始地址0x0000，数量8</li>
        <li>写DO：功能码0x05，地址为DO索引，数据为0xFF00（开）或0x0000（关）</li>
        <li>读DO：功能码0x01，起始地址0x0000，数量6</li>
    </ul>
    <p>所有报文都包含Modbus CRC16校验码。</p>
    
    <h3>5.3 华田屏幕GPIO接口</h3>
    <p>通过华田屏幕SDK提供的GPIO接口进行操作：</p>
    <ul>
        <li>使用GpioFactory::createDI()创建DI对象，调用getValue()读取状态</li>
        <li>使用GpioFactory::createDO()创建DO对象，调用getValue()读取状态，调用setValue()控制状态</li>
    </ul>
    <p>当KgGlobalData::ms_hardwareOemID为3时，使用华田屏幕GPIO接口；否则使用系统文件接口。</p>

    <h2>6. 硬件兼容性</h2>
    <p>CBMSIO类支持以下硬件平台：</p>
    <ul>
        <li>华田7寸屏：4个DI，4个DO</li>
        <li>华田10寸屏：16个DI，12个DO</li>
        <li>南北山设备：4个DI，4个DO</li>
    </ul>
    <p>通过KgGlobalData::ms_devDiCount和KgGlobalData::ms_devDoCount获取实际的DI/DO数量。</p>

    <h2>7. 使用示例</h2>
    <pre><code>// 创建CBMSIO对象
CBMSIO *pBmsIO = new CBMSIO();

// 初始化
if (pBmsIO->init() == 0)
{
    // 启动线程
    pBmsIO->start();
    
    // 获取IO模块指针
    CIOModule *pIOModule = ::getIOModule();
    
    // 读取DI状态
    bool di1State = (pIOModule->usDI[0] > 0);
    
    // 控制DO
    pIOModule->usDOCtrl[0] = 1; // 设置DO1为高电平
}

// 释放资源
delete pBmsIO;</code></pre>

    <h2>8. 注意事项</h2>
    <div class="warning">
        <p><strong>警告：</strong></p>
        <ul>
            <li>CBMSIO类的run()方法是一个无限循环，会一直运行直到程序结束</li>
            <li>在使用华田屏幕GPIO接口时，每次操作都会创建和销毁GPIO对象，可能会影响性能</li>
            <li>当硬件OEM ID不正确时，可能会导致IO操作失败</li>
            <li>修改DO状态时，应该修改usDOCtrl数组，而不是直接调用set_gpio()函数</li>
        </ul>
    </div>

    <h2>9. 版本历史</h2>
    <table>
        <tr>
            <th>版本</th>
            <th>日期</th>
            <th>变更内容</th>
        </tr>
        <tr>
            <td>0.1</td>
            <td>未知</td>
            <td>初始版本</td>
        </tr>
    </table>

    <h2>10. 相关类</h2>
    <ul>
        <li>CIOModule：IO模块类，用于存储DI/DO状态</li>
        <li>KgGlobalData：全局数据类，包含硬件配置信息</li>
        <li>GpioFactory：GPIO工厂类，用于创建DI/DO对象</li>
        <li>BasicGpio：基础GPIO类，提供GPIO操作接口</li>
    </ul>

    <hr>
    <p><em>文档生成日期：2024年5月</em></p>
</body>
</html>